/**
 * Copyright (C) 2017 - 2020 https://github.com/joewee
 * <p>
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * <p>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p>
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.bifrost.core;

import com.bifrost.annotation.ApiMapping;
import org.springframework.context.ApplicationContext;
import org.springframework.util.Assert;
import org.springframework.web.bind.annotation.RequestMethod;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;

/**
 *api仓库
 *
 *@author joewee
 *@version 1.0.0
 *@Date 2018/2/2 16:20
 */
public class ApiStore {
    private ApplicationContext applicationContext;
    private HashMap<String ,ApiRunnable> apiMap=new HashMap<>();

    public ApiStore(ApplicationContext applicationContext) {
        Assert.notNull(applicationContext,"applicationContext is null");
        this.applicationContext = applicationContext;
        loadApiFromSpringBeans();
    }

    /**
     * 对标记的API方法进行绑定
     */
    public void loadApiFromSpringBeans() {
        String[] names = applicationContext.getBeanDefinitionNames();
        Class<?> type;
        for (String name : names) {
            type = applicationContext.getType(name);
            for (Method method : type.getDeclaredMethods()) {
                ApiMapping apiMapping = method.getDeclaredAnnotation(ApiMapping.class);
                if (apiMapping!=null){
                    addApiItem(apiMapping,name,method);
                }
            }
        }
    }



    public ApiRunnable findApiRunable(String apiName){
        return apiMap.get(apiName);
    }

    /**
     * 添加api
     * @param apiMapping api配置
     * @param beanName bean 在springContext中的名称
     * @param method 方法
     */
    private void addApiItem(ApiMapping apiMapping, String beanName, Method method){
        ApiRunnable apiRunnable=new ApiRunnable();
        apiRunnable.setApiName(apiMapping.value());
        apiRunnable.setTargetMethod(method);
        apiRunnable.setTargetName(beanName);
        apiRunnable.setRequestMethod(apiMapping.requestMethod());
        apiMap.put(apiMapping.value(),apiRunnable);

    }
    public class ApiRunnable {
        private String apiName;
        private Method targetMethod;
        private String targetName;
        private Object target;
        private RequestMethod requestMethod;

        /**
         * 通过反射执行
         * @param args
         * @return
         * @throws IllegalAccessException
         * @throws InvocationTargetException
         */
        public Object run(Object...args)throws IllegalAccessException,InvocationTargetException {
            if (target==null){
                target = applicationContext.getBean(targetName);
            }
            return targetMethod.invoke(target,args);
        }

        public Class<?>[] getParamTypes(){
            return targetMethod.getParameterTypes();
        }
        public Object getTarget() {
            if (target==null){
                target = applicationContext.getBean(targetName);
            }
            return target;
        }

        public RequestMethod getRequestMethod() {
            return requestMethod;
        }

        public void setRequestMethod(RequestMethod requestMethod) {
            this.requestMethod = requestMethod;
        }

        public void setTarget(Object target) {
            this.target = target;
        }

        public String getApiName() {
            return apiName;
        }

        public void setApiName(String apiName) {
            this.apiName = apiName;
        }

        public Method getTargetMethod() {
            return targetMethod;
        }

        public void setTargetMethod(Method targetMethod) {
            this.targetMethod = targetMethod;
        }

        public String getTargetName() {
            return targetName;
        }

        public void setTargetName(String targetName) {
            this.targetName = targetName;
        }
    }
}
